#include <iostream>
#include <thread>
#include <list>
#include <mutex>
using namespace std;


/*
 P7 互斥量 死锁
(1)互斥量(mutex) 的基本概念
(2)互斥量的用法 (mutex)
    (2.1) lock()  unlock()
    (2.2) std::lock_guard()类模板
(3)死锁(mutex)
    (3.1) 死锁演示
    (3.2) 死锁的一般解决方案
    (3.3) std::lock()函数模板
    (3.4) std::lock_guard()的std::adopt_lock参数
*/
class MessageList
{
private:
    mutex my_mutex1;
    mutex my_mutex2;  //另外一个互斥量
    list<int> msgRecvQueue;
public:
    // 收集命令
    void inMsgRecvQueue()
    {
        for(int i=0;i<100000;i++)
        {
            my_mutex1.lock();
            my_mutex2.lock();    //
            cout<<"inMsgRecvQueue()执行,插入一个元素 : command= "<<i<<endl;
            msgRecvQueue.push_back(i);
            my_mutex2.unlock();  // 
            my_mutex1.unlock();
        }
    }
    bool outMsgLULProc(int & command)
    {
        //临时对象 
        //std::lock_guard<std::mutex> sbguard(my_mutex); // lock_guard 的构造函数 执行了 mutex::lock()
                                                        // lock_guard 的析构函数 执行了mutex::unlock()
        my_mutex2.lock(); //
        my_mutex1.lock();
        if(!msgRecvQueue.empty())
        {
            command = msgRecvQueue.front();
            msgRecvQueue.pop_front();
            my_mutex1.unlock();
            my_mutex2.unlock();  //
            return true;
        }
        my_mutex1.unlock();
        my_mutex2.unlock(); //
        return false;
    }
    void outMsgRecvQueue()
    {
        for(int i=0;i<100000;i++)
        {
            //my_mutex.lock();
            int command;
            if(outMsgLULProc(command))
            {
                //int command = msgRecvQueue.front(); //返回第一个元素，但不检查元素是否存在
                //msgRecvQueue.pop_front();
                cout<<"outMsgRecvQueue()执行,command = "<<command<<endl;
            }
            else
            {
                cout<<"outMsgRecvQueue()执行,但目前消息队列为空格"<<endl;
            }
            //my_mutex.unlock();
        }
        cout<<"outMsgRecvQueue()执行完成"<<endl;
    }
};

int main()
{
    // 保护共享数据 , 操作时把共享数据锁住，其他操作共享数据必须等待解锁
    /*一 互斥量(mutex) 的基本概念
        互斥量 是 一把锁
        多个线程 会用 lock() 尝试 加锁，只有一个线程等锁住;其他线程没锁成功，则会阻塞在lock()

        互斥量(mutex) 使用要小心，保护数据不多不少

    */
    int myval = 1;
    int & ref= myval;
    char mybuf[] = "this is a test";
    
     /*二 互斥量(mutex) 的用法
        步骤: lock()  操作共享数据 unlock()
        lock()和unlock()要成对使用

        有lock() 忘记 unlock() 的 情况很难排查(if 条件)

        std::lock_guard: 直接取代 lock() unlock()
        用了lock_guard 后就不能使用 lock() 和 unlock() 

        //临时对象 

        std::lock_guard<std::mutex> sbguard(my_mutex); 
        // lock_guard 的构造函数 执行了 mutex::lock()
        // lock_guard 的析构函数 执行了mutex::unlock()

    */

    /*三 死锁
        (3.1) 死锁演示
            C++ 
                两把锁 (至少两个互斥量 才能产生 死锁问题) 锁 Lock1  lock2
                两个线程 A B
                (1) A 线程先锁 lock1,再锁 lock2
                (2) B 线程先锁 lock2,再锁 lock1
                会发生 死锁

        (3.2) 死锁的一般解决方案

            只要保证 互斥量 上锁顺序一致，就不会死锁

        (3.3) std::lock()函数模板
            功能:一次锁住至少两个或者两个以上的互斥量
            它不存在因为 锁的顺序问题 导致的死锁风险

            std::lock() 如果有一个锁 没锁住，则会释放所有的锁。在原地等待

            std::lock(lock1,lock2);

        (3.4) std::lock_guard()的std::adopt_lock参数
       
            std::lock(lock1,lock2);
            
            //使用后 std::adopt_lock 此时不会 锁
            std::lock_guard<std::mutex> guard1(lock1,std::adopt_lock)
            std::lock_guard<std::mutex> guard2(lock2,std::adopt_lock)
    */
    MessageList msgList;
    //第二个参数是引用，保证 线程里面使用的是用一个对象
    thread t1(&MessageList::inMsgRecvQueue,&msgList);
    thread t2(&MessageList::outMsgRecvQueue,&msgList);
    t1.join();
    t2.join();

}